import { pushTarget, popTarget } from "./dep";
import { queueWatcher } from "./schedular";
let id=0;
export default class Watcher {
  constructor(vm, exprOrFn, callback, options) {
    this.vm = vm;
    this.callback = callback;
    this.options = options;
    this.user = options.user;
    this.id = id++;
    this.sync = options.sync; // 同步执行
    this.lazy = options.lazy; //定义是computed
    this.dirty = this.lazy; //定义使用缓存

    if(typeof exprOrFn === "function"){
      this.getter = exprOrFn;
    }else{
      this.getter = function(){
        let path = exprOrFn.split(".");
        let temp = vm;
        for(let i = 0; i <path.length; i++){
          temp = temp[path[i]] //用户定义的watcher，exprOrFn传入的是字符串，将字符串解析成对应vm身上的handler函数
        }
        return temp;
      }
    }
    
    // this.getter = exprOrFn; // 将传递过来的第二个参数，即updateComponent函数放到getter上
    this.depsId = new Set(); // watcher存放的dep不能重复
    this.deps = []; //watcer的deps属性里面存放dep
    this.value = this.lazy ? undefined : this.get(); //根据lazy属性决定是否一开始就调用get
  }
  get() {
    pushTarget(this); // 把当前的watcher存起来
    let value = this.getter.call(this.vm); //渲染watcher执行
    popTarget(); //移除watcher
    return value;
  }
  update() {
    if(this.sync){
      this.run();
    }else if(this.lazy){ // 计算属性computed发生更新
      this.dirty = true;
    }else{
    // 异步更新watcher
      queueWatcher(this);
    // this.get();
    }
  }
  addDep(dep) {
    let id = dep.id;
    // 避免watcher里存放重复的dep
    if (!this.depsId.has(id)) {
      this.depsId.add(id);
      this.deps.push(dep); // watcher存放dep
      dep.addSub(this); //dep里面存放当前watcher 即this
    }
  }
  run() {
    let oldValue = this.value; //第一次渲染的值
    let newValue = this.get();
    this.value = newValue;
    if(this.user){ //如果是用户watcher
      // 渲染watcher的callback为空， 用户watcher 会传入一个执行函数
      this.callback.call(this.vm, newValue, oldValue);
    }
  }
  evaluate(){
    this.value = this.get();
    this.dirty = false; //依赖的数据没有变化，直接使用缓存数据
  }
  depend(){
    let i = this.deps.length;
    while(i--){
      this.deps[i].depend()
    }
  }
}
